el-table-column
<template>
  <div>
    <el-container class="container-box">
      <el-main class="container-content">
        <el-descriptions title="" size="default" :column="1">
          <el-descriptions-item label="比赛名称:">
            {{ match?.name }}
          </el-descriptions-item>
          <el-descriptions-item label="比赛时间:">
            {{ TimeYH1(match?.activitystarttime) }} -
            {{ TimeYH1(match?.activityendtime) }}
          </el-descriptions-item>
        </el-descriptions>
        <el-form size="default" :inline="true">
          <!-- 比赛下拉框 -->
          <el-form-item label="选择比赛">
            <el-select
              v-model="match"
              value-key="ID"
              clearable
              placeholder="请选择"
              style="width: 240px"
            >
              <el-option
                v-for="item in selectMatch"
                :key="item"
                :label="item.name"
                :value="item"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button @click="start" type="primary" size="default">
              查询
            </el-button>
            <el-button
              @click="ActivityExcal4(false)"
              type="primary"
              size="default"
            >
              导出Excel
            </el-button>
            <el-button
              @click="ActivityExcal4(true)"
              type="primary"
              size="default"
            >
              导出Excel（含源码）
            </el-button>
            <el-button @click="showDialog" type="warning" size="default">
              重新运行学员代码
            </el-button>

            <el-button>总人数: {{ user.length }}</el-button>
            <el-button>判题中人数: {{ waitingCount + activeCount }}</el-button>
            <el-button>提交人数: {{ matchuserquestioncount }}</el-button>
          </el-form-item>
          <el-form-item>
            <span style="margin-left: 10px; color: #409eff; font-size: 14px">
              💡 该页只展示提交过题目的学生成绩
            </span>
          </el-form-item>
        </el-form>
        <el-table
          border
          :data="
            userScore.slice(
              formquery._offset,
              formquery._offset + formquery._limit
            )
          "
          height="66vh"
          style="width: 100%"
          :row-class-name="rowClassName"
          @row-click="clickTableRow"
          row-key="ID"
          :expand-row-keys="expand"
        >
          <template v-slot:empty>
            <el-empty
              image="https://cdn-hterobot.oss-cn-qingdao.aliyuncs.com/image/ojSass-empty.png"
            />
          </template>
          <el-table-column type="expand">
            <template #default="{ row }">
              <el-table
                border
                :data="row.matchuserquestion"
                style="width: 100%"
              >
                <el-table-column
                  type="index"
                  label="题目"
                  width="80"
                ></el-table-column>
                <el-table-column
                  prop="question.name"
                  label="题目名称"
                ></el-table-column>
                <el-table-column
                  prop="question.type"
                  width=""
                  label="题目类型"
                ></el-table-column>
                <el-table-column
                  prop="question.score"
                  width=""
                  label="题目分数"
                ></el-table-column>
                <el-table-column prop="info" label="答案">
                  <template #default="scope">
                    <span v-if="scope.row.type === '判断'">{{
                      scope.row.info?.answer ? "正确" : "错误"
                    }}</span>
                    <span v-else>
                      {{ scope.row.info?.answer }}
                    </span>
                  </template>
                </el-table-column>
                <el-table-column prop="df" width="" label="题目得分">
                  <template #default="{ row }">
                    <!-- df 有存在 null 的情况 -->
                    {{ row.df || 0 }}
                  </template>
                </el-table-column>
              </el-table>
            </template>
          </el-table-column>
          <el-table-column prop="name" label="用户名"></el-table-column>
          <el-table-column prop="ID" label="用户ID"></el-table-column>
          <el-table-column prop="phonenum" label="手机"></el-table-column>
          <el-table-column prop="df" label="总分"></el-table-column>
          <el-table-column
            prop="updatedAt"
            :formatter="formatStartTime"
            label="答题时间"
          ></el-table-column>
          <el-table-column
            prop="updatedAt"
            :formatter="formatEndTime"
            label="交卷时间"
          ></el-table-column>
          <el-table-column label="操作" width="130">
            <template #default="{ row }">
              <!-- :disabled="!row.matchuserquestion.length" -->
              <el-button
                v-show="row.matchuserquestion.length"
                type="danger"
                @click="deluserquestion(row.name, row.ID, Number(activity?.ID))"
                size="default"
              >
                删除答题
              </el-button>
            </template>
          </el-table-column>
        </el-table>
        <el-pagination
          class="pager"
          @current-change="handleCurrentChange"
          :page-size="Number(formquery._limit)"
          layout="total,prev, pager, next"
          :total="user.length"
        >
        </el-pagination>
        <!-- 重新运行学员代码 -->
        <el-dialog v-model="dialogVisible" title="重新运行学员代码" width="800">
          <template #header>
            <span class="icon-text el-dialog__header-title">
              <el-icon class="text-primary">
                <Guide />
              </el-icon>
              重新运行学员代码
            </span>
          </template>
          <el-form :model="runactivityform" label-width="auto" size="default">
            <el-row class="margin-top20">
              <el-col :span="12">
                <el-form-item label="是否使用缓存">
                  <el-switch v-model="runactivityform.iscatch" />
                </el-form-item>
                <el-form-item label="是否运行已运行过的">
                  <el-switch v-model="runactivityform.isinfo" />
                </el-form-item>
                <el-form-item label="是否开启O2">
                  <el-radio-group v-model="runactivityform.iso2">
                    <el-radio :value="true" size="default">是</el-radio>
                    <el-radio :value="false" size="default">否</el-radio>
                  </el-radio-group>
                </el-form-item>
                <!-- 成员默认全选 -->
                <el-form-item label="选择运行的学员">
                  <el-select
                    v-model="runactivityform.userid"
                    collapse-tags
                    multiple
                    filterable
                    class="m-2"
                    clearable
                    placeholder="Select"
                  >
                    <el-option
                      v-for="v in user"
                      :key="v?.ID"
                      :label="v?.name"
                      :value="Number(v?.ID)"
                    />
                  </el-select>
                </el-form-item>
                <el-form-item class="margin20" label="">
                  <el-button
                    type="primary"
                    size="default"
                    :isabled="disabled"
                    @click="runactivity"
                  >
                    重新运行学员代码
                  </el-button>
                </el-form-item>
              </el-col>
              <el-col :span="12">
                <el-form-item label="运行状态">
                  <el-progress
                    type="circle"
                    v-if="runactivityoption.length"
                    :percentage="
                      ((runactivityoption.length - runactivityoption.index) /
                        runactivityoption.length) *
                      100
                    "
                  >
                    <span class="percentage-value">
                      {{ runactivityoption.length - runactivityoption.index }} /
                      {{ runactivityoption.length }}
                    </span>
                  </el-progress>
                  <div v-else>
                    <el-progress type="circle">
                      <span class="percentage-value"> 0 </span>
                    </el-progress>
                  </div>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
          <template #footer>
            <span class="dialog-footer">
              <el-button type="primary" @click="dialogVisible = false">
                关闭
              </el-button>
            </span>
          </template>
        </el-dialog>
      </el-main>
    </el-container>
  </div>
</template>

<script setup lang="ts">
import { ElMessage, ElMessageBox } from "element-plus";
import { computed, inject, onMounted, reactive, Ref, ref, watch } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import {
  asyncdelTeamMatchUserQuestion,
  asyncgetmatchjob,
  asyncselectmatchuserquestion,
  asyncTeamRunActivity,
  asyncteamselectactivitys,
} from "../../../api";
import { Irunactivityoption, Storekey } from "../../../interface";
import Studycodemodel from "../../../mysql_interface/studycodemodel";
import { getExceltodownload2 } from "../../../util/excel";
import { TimeYH1 } from "../../../util/time";
import { groupBy } from "lodash-es";

const p = defineProps<{ matchId: number; data: Studycodemodel.Iactivity }>();

const router = useRouter();
const store = useStore(Storekey);
const getuser = computed(() => {
  return store.state.user;
});
const teamId = computed(() => {
  return getuser.value?.teamid;
});
const matchuserquestioncount = computed(() => {
  const obj = groupBy(matchuserquestion.value, "userid");
  return Object.keys(obj).length;
});
const match = ref<Studycodemodel.Iactivity>(p.data);
watch(
  () => p.matchId,
  (e) => {
    match.value = { ID: e };
  }
);
watch(
  () => match.value,
  (e) => {
    matchUserQuestion();
  }
);
const selectMatch = ref<Studycodemodel.Iactivity[]>([]);

onMounted(async () => {
  await start();
  if (p.matchId) {
    match.value = p.data;
  } else {
    match.value = selectMatch.value[0];
  }
});

const waitingCount = ref(0);
const activeCount = ref(0);

async function start() {
  // 获取比赛列表
  const { rows, count } = await asyncteamselectactivitys({
    teamid: teamId.value,
  });
  selectMatch.value = rows;
  matchUserQuestion();
  const d = await asyncgetmatchjob({ activityid: p.matchId });
  waitingCount.value = d.waitingCount || 0;
  activeCount.value = d.activeCount || 0;
}
const activity = ref<Studycodemodel.Iactivity>();
const question = ref<Studycodemodel.Iquestion[]>([]);
// 答题记录
const matchuserquestion = ref<Studycodemodel.Imatchuserquestion[]>([]);
const user = ref<Studycodemodel.Iuser[]>([]);
const team = ref<Studycodemodel.Iteam[]>([]);
const teamuser = ref<Studycodemodel.Iteamuser[]>([]);
async function matchUserQuestion() {
  if (match.value.ID) {
    const data = await asyncselectmatchuserquestion({ ID: match.value.ID });
    // const data = await asyncteamselectmatchuserquestion({ activityid: match.value.ID, teamid: teamId.value });
    activity.value = data.activity;
    question.value = data.question;
    matchuserquestion.value = data.userquestion;
    // 过滤参赛选手
    user.value = data.user;
    if (match.value.activityuser?.length) {
      user.value = data.user.filter((e) =>
        match.value.activityuser?.find((actUser) => actUser.userid === e.ID)
      );
    }
    team.value = data.team;
    teamuser.value = data.teamuser;
  }
}
const formquery = reactive({ _limit: 30, _offset: 0 });
function handleCurrentChange(val: number) {
  formquery._offset = Number(formquery._limit) * (val - 1);
}

interface matchuserquestionView extends Studycodemodel.Iuser {
  matchuserquestion: (Studycodemodel.Imatchuserquestion &
    Studycodemodel.Iquestion)[];
  df: number;
  starttime: Date;
  endtime: Date;
}
// <matchuserquestionView[]>
// 表格数据
const userScore = computed(() => {
  return user.value
    .filter((e) => {
      // 只显示有提交记录的用户
      return matchuserquestion.value.some((muq) => muq.userid === e.ID);
    })
    .map((e) => {
      // 初始化
      let _df = 0;
      let starttime: Date | string | undefined;
      let endtime: Date | string | undefined;
      const _matchuserquestion = matchuserquestion.value
        .filter((n) => n.userid === e.ID)
        .map((muq) => {
          // 题目详情
          const _question = question.value.find((q) => q.ID === muq.questionid);
          const { df = 0, createdAt = undefined } = muq;
          _df = _df + df;
          if (createdAt) {
            starttime = !starttime ? createdAt : starttime;
            endtime = !endtime ? createdAt : endtime;
            // 判断
            if (starttime < createdAt) {
              endtime = createdAt;
            }
          }
          return { ...muq, question: _question };
        });
      return {
        ...e,
        matchuserquestion: _matchuserquestion,
        df: _df,
        starttime,
        endtime,
      };
    })
    .sort((a, b) => {
      if (!a.endtime && !b.endtime) {
        return 0;
      } else if (!a.endtime) {
        return 1;
      } else if (!b.endtime) {
        return -1;
      } else {
        return a.endtime < b.endtime ? -1 : 1;
      }
    })
    .sort((a, b) => b.df - a.df);
});

function ActivityExcal4(usecontext: boolean) {
  const q = question.value.map((e, k) => `第${k + 1}题`);
  const questionCount = question.value.length;

  // 预处理团队数据映射
  const userTeamMap = new Map<number, { teams: string; teamType: string }>();
  const key = ["团员", "管理员", "教师"];
  console.log(21213213);
  user.value.forEach((u) => {
    const userTeams = teamuser.value
      .filter((tu) => tu.userid === u.ID)
      .map((tu) => {
        const team1 = team.value.find((t) => t.ID === tu.teamid);
        return { name: team1?.name || "", isAdmin: tu.isAdmin };
      });

    const teams = userTeams
      .map((t) => t.name)
      .filter(Boolean)
      .join(",");
    const teamType =
      userTeams.length > 0
        ? key[Math.max(...userTeams.map((t) => t.isAdmin!))] || "管理员"
        : "管理员";

    userTeamMap.set(u.ID!, { teams, teamType });
  });

  // 预处理用户答题数据映射
  const userQuestionsMap = new Map<number, any[]>();
  matchuserquestion.value.forEach((muq) => {
    if (!userQuestionsMap.has(muq.userid!)) {
      userQuestionsMap.set(muq.userid!, []);
    }
    userQuestionsMap.get(muq.userid!)!.push(muq);
  });

  const sheet1: any[][] = [];
  const sheet2: any[][] = [];
  const sheet4: any[][] = [];
  const ather: any[][] = [];

  // 初始化统计数组
  const scoreStats = {
    all: 0,
    all1: new Array(questionCount).fill(0), // 100分
    all2: new Array(questionCount).fill(0), // 71-99分
    all3: new Array(questionCount).fill(0), // 20-70分
    all4: new Array(questionCount).fill(0), // 1-19分
    all5: new Array(questionCount).fill(0), // 0分
  };

  // 主循环：一次遍历处理所有数据
  user.value.forEach((user) => {
    const { ID, name, xingming, phonenum, info } = user;
    const userquestions = userQuestionsMap.get(ID!) || [];

    if (userquestions.length === 0) return;

    scoreStats.all++;
    const teamInfo = userTeamMap.get(ID!) || { teams: "", teamType: "管理员" };

    // 计算总分
    const totalScore = userquestions.reduce((sum, uq) => sum + (uq.df || 0), 0);

    // 构建基础行数据
    const baseRow = [
      name,
      xingming,
      teamInfo.teams,
      ID,
      phonenum,
      info?.school,
      info?.grade,
      info?.class,
      teamInfo.teamType,
      totalScore,
    ];

    const t1 = [...baseRow];
    const t2 = [...baseRow];
    const t4 = [...baseRow];

    // 按题目顺序处理答案
    question.value.forEach((question, index) => {
      const userquestion =
        userquestions.find((uq) => uq.questionid === question.ID) || {};
      const {
        df = 0,
        resultdataarr = [],
        resultdata = "CE",
        context,
        type,
        info: answerInfo,
      } = userquestion;

      // 统计得分段
      if (df === 100) {
        scoreStats.all1[index]++;
      } else if (df > 70) {
        scoreStats.all2[index]++;
      } else if (df > 19) {
        scoreStats.all3[index]++;
      } else if (df > 0) {
        scoreStats.all4[index]++;
      } else {
        scoreStats.all5[index]++;
      }

      // 添加到各个sheet
      t1.push(df);
      t4.push(context?.slice(0, 30000));

      if (type === "代码") {
        if (resultdata === "CE" || !context || !resultdata) {
          t2.push("Compile Error");
        } else {
          t2.push(`${df} (${resultdataarr.join(",")})`);
        }
      } else {
        t2.push(`${df} (${(answerInfo as any)?.answer || ""})`);
      }
    });

    sheet1.push(t1);
    sheet2.push(t2);
    sheet4.push(t4);
  });

  // 构建统计数据
  const { all, all1, all2, all3, all4, all5 } = scoreStats;
  const getPercentage = (count: number) =>
    Math.floor((count / all) * 10000) / 100 + "%";

  const tj = [
    ["题目得分段统计", ...q],
    ["100%分段", ...all1.map(getPercentage)],
    ["71%-99%分段", ...all2.map(getPercentage)],
    ["20%-70%分段", ...all3.map(getPercentage)],
    ["1%-19%分段", ...all4.map(getPercentage)],
    ["0分段", ...all5.map(getPercentage)],
    [],
    [],
    [],
    ["参赛人员占比", Math.floor((all / user.value.length) * 10000) / 100 + "%"],
  ];

  const headers = [
    "昵称",
    "姓名",
    "团队",
    "ID",
    "手机号",
    "学校",
    "年级",
    "班级",
    "团队角色",
    "总得分",
    ...q,
  ];

  const dowm: Record<string, any[][]> = {
    得分统计: [headers, ...sheet1.sort((a, b) => b[8] - a[8]), ...ather],
    得分点: [headers, ...sheet2.sort((a, b) => b[8] - a[8]), ...ather],
    分段统计: tj,
  };

  if (usecontext) {
    dowm.代码 = [headers, ...sheet4.sort((a, b) => b[8] - a[8]), ...ather];
  }

  getExceltodownload2(dowm, p.data.name || "");
}

async function deluserquestion(name: string, ID: number, activityid: number) {
  if (ID) {
    ElMessageBox.confirm(`是否删除成员 ${name} 的答题记录`, "提示", {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }).then(async () => {
      try {
        await asyncdelTeamMatchUserQuestion({
          teamid: teamId?.value!,
          userid: ID,
          activityid,
        });
        ElMessage.success("删除成功");
        start();
      } catch (e: any) {
        ElMessage.info("删除成功");
      }
    });
  } else {
    ElMessage.warning("没有找到此用户");
  }
}

// 展开行
const expand = ref<(number | undefined)[]>([]);
const selectedRow = ref({});
function clickTableRow(row: matchuserquestionView) {
  if (expand.value.includes(row.ID)) {
    expand.value = expand.value.filter((e) => e !== row.ID);
  } else {
    expand.value.push(row.ID);
  }
  if (selectedRow.value !== row) {
    selectedRow.value = row;
    return;
  }
  selectedRow.value = {};
}
function rowClassName({
  row,
  rowIndex,
}: {
  row: any;
  rowIndex: number;
}): string {
  let className = "cursor-pointer";
  if (selectedRow.value == row) {
    className = "highlight-row cursor-pointer"; //
  }
  return className;
}

// formatter
function formatStartTime({ starttime }: matchuserquestionView) {
  return starttime ? TimeYH1(starttime) : "-";
}
function formatEndTime({ endtime }: matchuserquestionView) {
  return endtime ? TimeYH1(endtime) : "-";
}

// 重新运行学员代码
const dialogVisible = ref(false);
const runactivityform = reactive<{
  ID: number;
  iscatch?: boolean | undefined;
  isinfo?: boolean | undefined;
  iso2?: boolean | undefined;
  userid?: number[] | undefined;
}>({
  iscatch: true,
  isinfo: true,
  iso2: true,
  userid: [],
  ID: match.value.ID!,
});

function showDialog() {
  dialogVisible.value = true;
  runactivityform.userid = user.value.map((e) => Number(e?.ID)) || [];
  runactivityform.ID = match.value.ID!;
  activityprocess();
}
// const users = computed(() => {
//     const t1 = selectedActivity.value?.activityuser?.map(c => c.user) || []
//     const t2 = selectedActivity.value?.team?.user
//     const t3 = selectedActivity.value?.team?.teamuser?.map(c => c.user) || []
//     return uniq(t1.concat(t2).concat(...t3).filter(c => c), e => {
//         return e?.ID
//     })
// })
async function runactivity() {
  // 默认选中全部、全部都不选也是全部
  let userid = runactivityform.userid;
  if (!runactivityform?.userid?.length) {
    userid = user.value.map((e) => Number(e?.ID));
  }
  try {
    // team接口
    await asyncTeamRunActivity({
      ...runactivityform,
      userid,
      teamid: teamId.value!,
    });
    activityprocess();
    ElMessage.success("任务已添加");
  } catch (error: any) {
    ElMessage.error(`任务添加失败: ${error.message || error}`);
    console.error(error);
  }
}
const runactivityoptions =
  inject<Ref<Irunactivityoption[]>>("runactivityoptions");
const disabled = computed(() => {
  return false;
});

const runactivityoption = computed((e) => {
  return (
    runactivityoptions?.value?.find((e) => e.activityid === match.value.ID) || {
      activityid: match.value.ID,
      index: 0,
      length: 0,
    }
  );
});
function activityprocess() {
  store.state.socket.emit("activityprocess", match.value.ID);
}
</script>

<style scoped>
.container-box {
  /* padding: var(--container-margin) 5% 0;
  margin: var(--container-margin) 0rem 0 0rem; */
  /* 外边距 */
  --container-margin: 20px;
  --container-header-height: 10vh;
  --container-bg-color: #fff;
}

.container-content {
  background: var(--container-bg-color);
  padding: 0;
  min-width: 100%;
}

.match-search-btn {
  margin-bottom: var(--container-margin);
}

.pager {
  margin-top: var(--container-margin);
  float: right;
}
</style>
